package com.xiaoyu.lanling.feature.fate

import `in`.srain.cube.util.internal.AppConsumer
import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.location.LocationManager
import android.os.Build
import android.os.CountDownTimer
import android.provider.Settings
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import com.amap.api.location.AMapLocation
import com.opensource.svgaplayer.SVGADrawable
import com.tbruyelle.rxpermissions2.RxPermissions
import com.xiaoyu.base.app.App
import com.xiaoyu.base.app.AppContext
import com.xiaoyu.base.app.GlobalUI
import com.xiaoyu.base.event.AppEventBus
import com.xiaoyu.base.event.SimpleEventHandler
import com.xiaoyu.base.model.User
import com.xiaoyu.base.utils.RxUtils
import com.xiaoyu.base.utils.extensions.dp
import com.xiaoyu.base.utils.extensions.setOnClickDebounceListener
import com.xiaoyu.base.utils.extensions.setViewTag
import com.xiaoyu.lanling.R
import com.xiaoyu.lanling.common.Constant
import com.xiaoyu.lanling.data.SvgaData
import com.xiaoyu.lanling.event.board.MainFateBoardTop3Event
import com.xiaoyu.lanling.event.coin.CoinAccostEvent
import com.xiaoyu.lanling.event.location.AddressEvent
import com.xiaoyu.lanling.event.main.EntrySettingEvent
import com.xiaoyu.lanling.event.ontv.OnTVHeaderInsertEvent
import com.xiaoyu.lanling.event.ontv.TVListEvent
import com.xiaoyu.lanling.event.ontv.TVLogger
import com.xiaoyu.lanling.event.videomatch.VideoMatchCheckEntryStatusEvent
import com.xiaoyu.lanling.event.videomatch.VideoMatchContinueMatchEvent
import com.xiaoyu.lanling.event.voicematch.VoiceMatchCheckEntryStatusEvent
import com.xiaoyu.lanling.event.voicematch.VoiceMatchContinueMatchEvent
import com.xiaoyu.lanling.feature.board.data.BoardDataCreator
import com.xiaoyu.lanling.feature.fate.model.TvFlipperAdapter
import com.xiaoyu.lanling.feature.location.LocationData
import com.xiaoyu.lanling.feature.location.LocationHelper
import com.xiaoyu.lanling.feature.main.data.HomeData
import com.xiaoyu.lanling.feature.ontv.data.TVData
import com.xiaoyu.lanling.feature.profile.VerifyDialogManager
import com.xiaoyu.lanling.feature.user.data.UserInfoData
import com.xiaoyu.lanling.feature.videomatch.data.VideoMatchData
import com.xiaoyu.lanling.feature.videomatch.dialog.AVFunctionalityTimeLimitOpenDialog
import com.xiaoyu.lanling.feature.videomatch.log.VideoMatchLogger
import com.xiaoyu.lanling.feature.voicematch.data.VoiceMatchData
import com.xiaoyu.lanling.feature.voicematch.log.VoiceMatchLogger
import com.xiaoyu.lanling.lifecycle.disposeOn
import com.xiaoyu.lanling.media.image.ImageViewUtil
import com.xiaoyu.lanling.media.image.UserImageLoadParam
import com.xiaoyu.lanling.router.Router
import com.xiaoyu.lanling.view.FragmentAdapter
import com.xiaoyu.lanling.view.OnShowFragment
import com.xiaoyu.lanling.view.UserAvatarDraweeView
import com.xiaoyu.lanling.widget.flipper.ViewFlipper2
import com.xiaoyu.lanling.widget.svga.SvgaBaseCallback
import com.xiaoyu.lanling.widget.tablayout.ScaleTabLayoutUtils
import com.xiaoyu.lib_av.state.CallStateManager
import io.reactivex.Observable
import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.fate_be_on_tv.*
import kotlinx.android.synthetic.main.fragment_main_fate_wrapper.*
import kotlinx.android.synthetic.main.view_main_fate_without_permission.*
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.util.concurrent.TimeUnit

class MainFateWrapperController(val fragment: OnShowFragment) {
    private var mPermissionDisposable: Disposable? = null
    private var mSvgaDisposable: Disposable? = null
    private val requestTag = Any()
    private val flipperAdapter: TvFlipperAdapter = TvFlipperAdapter()
    private var flipTimer: CountDownTimer? = null
    private var tvListData = TVData(10)

    private val viewFlippedListener = object : ViewFlipper2.OnViewFlipListener {
        override fun onViewFlipped(index: Int) {
            flipTimer?.cancel()
            if (index in 0 until flipperAdapter.dataList.size) {
                TVLogger.logShow()
                flipTimer = FlipCountDownTimer(flipperAdapter.dataList[index].lockScreenDurationSec * 1000L, 1000L)
                flipTimer?.start()
            }
        }

        override fun onFinish() {
            tvListData.queryNextPage()
        }
    }

    init {
        initView()
        initBind()
        initEvent()
        initPermission()
    }

    private fun initView() {
        fragment.flipper.setAdapter(flipperAdapter)
        fragment.flipper.viewFlippedListener = viewFlippedListener
    }

    private fun initPermission() {
        requestLocation()
    }

    private fun initBind() {
        fragment.ontv_how_button.setOnClickDebounceListener {
            val topActivity = App.getInstance().topActivity ?: return@setOnClickDebounceListener
            Router.instance.gotoOnTV(topActivity)
        }
        fragment.permission_button?.setOnClickDebounceListener { v ->
            if (enableLocation()) {
                requestLocationEvenIfNotAskAgain()
            } else {
                val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
                fragment.startActivityForResult(intent, Constant.RequestCode.LOCATION)
            }
        }
        fragment.accost_svga?.callback = object : SvgaBaseCallback() {
            override fun onFinished() {
                fragment.accost_svga?.visibility = View.GONE
            }
        }
        fragment.board_top3_layout?.setOnClickDebounceListener {
            fragment.activity?.let {
                Router.instance.gotoBoardActivity(it)
            }
        }
        fragment.preference?.setOnClickDebounceListener {
            fragment.activity?.let {
                Router.instance.gotoFatePreferenceActivity(it)
            }
        }
        fragment.voice_match_enter?.setOnClickDebounceListener {
            VoiceMatchData.checkEntryStatus(requestTag, Constant.From.HOME)
        }
        fragment.video_match_enter?.setOnClickDebounceListener {
            VideoMatchData.checkEntryStatus(requestTag, Constant.From.HOME)
        }
    }

    private fun initEvent() {
        AppEventBus.bindContainerAndHandler(fragment.activity, object : SimpleEventHandler() {

            @Subscribe(threadMode = ThreadMode.MAIN)
            fun onEvent(event: MainFateBoardTop3Event) {
                loadBoardTop3Avatar(event.userList)
            }

            @Subscribe(threadMode = ThreadMode.MAIN)
            fun onEvent(event: AddressEvent) {
                if (event.isNotFromThisRequestTag(requestTag)) {
                    return
                }
                UserInfoData.setLocation(requestTag, event.province, event.city, event.county)
            }

            @Subscribe(threadMode = ThreadMode.MAIN)
            fun onEvent(event: CoinAccostEvent) {
                processAccost(event)
            }

            @Subscribe(threadMode = ThreadMode.MAIN)
            fun onEvent(event: TVListEvent) {
                flipperAdapter.setData(event.tvList)
            }

            @Subscribe(threadMode = ThreadMode.MAIN)
            fun onEvent(event: OnTVHeaderInsertEvent) {
                flipperAdapter.insert(fragment.flipper.displayedChild, event.tvFlipItem)
            }

            @Subscribe(threadMode = ThreadMode.MAIN)
            fun onEvent(event: EntrySettingEvent) {
                updateVideoMatchStatus(event)
            }

            @Subscribe(threadMode = ThreadMode.MAIN)
            fun onEvent(event: VoiceMatchCheckEntryStatusEvent) {
                if (event.isNotFromThisRequestTag(requestTag)) {
                    return
                }
                val isCalling = CallStateManager.isNotIdle
                VoiceMatchLogger.click(event.from, event.entry.available, isCalling)
                when {
                    isCalling -> GlobalUI.getInstance().showToast(R.string.call_is_calling_toast)
                    event.entry.available.not() -> AVFunctionalityTimeLimitOpenDialog.show(event.entry.dialogMessage)
                    else -> Router.instance.gotoVoiceMatchWaitingActivity(fragment.activity)
                }
            }

            @Subscribe(threadMode = ThreadMode.MAIN)
            fun onEvent(event: VideoMatchCheckEntryStatusEvent) {
                if (event.isNotFromThisRequestTag(requestTag)) {
                    return
                }
                val status = if (event.entry.available) "open" else "close"
                VideoMatchLogger.logVideoMatchClick(event.from, status, CallStateManager.isNotIdle)
                if (event.entry.available.not()) {
                    AVFunctionalityTimeLimitOpenDialog.show(event.entry.dialogMessage)
                    return
                } else if (CallStateManager.isNotIdle) {
                    GlobalUI.getInstance().showToast(R.string.call_is_calling_toast)
                    return
                }
                Router.instance.gotoVideoMatchWaitingActivity(fragment.activity)
            }

            @Subscribe(threadMode = ThreadMode.MAIN)
            fun onEvent(event: VoiceMatchContinueMatchEvent) {
                VoiceMatchData.checkEntryStatus(requestTag, Constant.From.VOICE_MATCH)
            }

            @Subscribe(threadMode = ThreadMode.MAIN)
            fun onEvent(event: VideoMatchContinueMatchEvent) {
                VideoMatchData.checkEntryStatus(requestTag, Constant.From.VIDEO_MATCH)
            }
        })
    }

    private fun updateVideoMatchStatus(event: EntrySettingEvent) {
        fragment.voice_match_indicator.isEnabled = event.voiceMatchEntry.available
        fragment.voice_match_indicator.text = event.voiceMatchEntry.tip
        fragment.voice_match_enter.setViewTag(event.voiceMatchEntry)

        fragment.video_match_indicator.isEnabled = event.videoMatchEntry.available
        fragment.video_match_indicator.text = event.videoMatchEntry.tip
        fragment.video_match_enter.setViewTag(event.videoMatchEntry)
    }

    private fun isLocatable(): Boolean {
        return enableLocation() && hasLocationPermission();
    }

    private fun enableLocation(): Boolean {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            val locationManager = (AppContext.getContext().getSystemService(Context.LOCATION_SERVICE) as? LocationManager)
            return locationManager?.isLocationEnabled == true
        } else {
            var locationMode = 0
            try {
                locationMode = Settings.Secure.getInt(AppContext.getContext().contentResolver, Settings.Secure.LOCATION_MODE)
            } catch (t: Throwable) {
                t.printStackTrace()
            } finally {
                return locationMode != Settings.Secure.LOCATION_MODE_OFF
            }
        }
    }

    private fun hasLocationPermission(): Boolean {
        return ActivityCompat.checkSelfPermission(AppContext.getContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
                || ActivityCompat.checkSelfPermission(AppContext.getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
    }

    fun updatePermissionLayout() {
        val locatable = isLocatable()
        fragment.permission_layout?.visibility = if (locatable) View.GONE else View.VISIBLE
    }

    private fun requestLocationEvenIfNotAskAgain() {
        if (fragment.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {
            requestLocation()
        } else {
            showRequestPermissionDialog()
        }
    }

    private fun showRequestPermissionDialog() {
        fragment.activity?.let {
            AlertDialog.Builder(it)
                    .setTitle(AppContext.getString(R.string.fate_permission_setting_dialog_title))
                    .setMessage(AppContext.getString(R.string.fate_permission_setting_dialog_message))
                    .setPositiveButton(AppContext.getString(R.string.fate_permission_setting_dialog_positive_button)) { _, _ ->
                        App.getInstance().topActivity?.let { Router.instance.gotoApplicationDetailsSettings(it) }
                    }
                    .setNegativeButton(AppContext.getString(R.string.action_cancel)) { _, _ -> }
                    .show()
        }
    }

    fun requestLocation() {
        fragment.activity?.let {
            mPermissionDisposable = RxPermissions(it)
                    .request(Manifest.permission.ACCESS_FINE_LOCATION)
                    .subscribe { granted ->
                        updatePermissionLayout()
                        if (granted) {
                            getLocation()
                        }
                    }
        }
    }

    private fun getLocation() {
        LocationHelper().getLocation(object : AppConsumer<AMapLocation> {
            override fun accept(location: AMapLocation?) {
                if (location == null || location.longitude <= 0 || location.latitude <= 0) {
                    return
                }
                LocationData.getAddress(requestTag, location.longitude, location.latitude)
            }
        })
    }

    private fun processAccost(event: CoinAccostEvent) {
        showAccostSvga()
        if (event.showVerifyAndVoiceDialog) {
            VerifyDialogManager.showDialog()
        }
    }

    private fun loadBoardTop3Avatar(avatarList: List<User>) {
        // NOTE hide board avatars if few people on board
        fragment.top3_avatar_wrapper_layout.visibility = if (avatarList.size < MainFateFragment.MAX_TOP_COUNT) View.GONE else View.VISIBLE

        for ((i, user) in avatarList.withIndex()) {
            when (i) {
                MainFateFragment.TOP_1_INDEX -> loadBoardTopAvatar(fragment.top1_avatar, user)
                MainFateFragment.TOP_2_INDEX -> loadBoardTopAvatar(fragment.top2_avatar, user)
                MainFateFragment.TOP_3_INDEX -> loadBoardTopAvatar(fragment.top3_avatar, user)
            }
        }
    }

    private fun loadBoardTopAvatar(draweeView: UserAvatarDraweeView, user: User) {
        ImageViewUtil.loadImage(draweeView, UserImageLoadParam.newBuilder().setUserAndDPRound(user, 24).setCornersRadius(12.dp)?.build())
    }

    fun onStart() {
        val fragmentList = mutableListOf<OnShowFragment>()
        fragmentList.add(MainFateFragment())
        fragmentList.add(MainFateNearbyFragment())

        val titles = listOf(AppContext.getString(R.string.main_tab_sub_fate), AppContext.getString(R.string.main_tab_sub_fate_nearby))
        ScaleTabLayoutUtils.createNormalScaleTabLayout(fragment.main_fate_wrapper_magic_indicator, fragment.main_tab_fate_view_pager, titles, false)

        val fragmentAdapter = FragmentAdapter(fragment.childFragmentManager, fragmentList, titles)
        fragment.main_tab_fate_view_pager.offscreenPageLimit = fragmentList.size
        fragment.main_tab_fate_view_pager.adapter = fragmentAdapter
        requestBoardTop3()
        tvListData.queryNextPage()
        HomeData.getEntrySetting(requestTag)
        startOnTVDataFetchTimer()
    }

    private fun startOnTVDataFetchTimer() {
        Observable.interval(10, 10, TimeUnit.SECONDS)
                .subscribe {
                    if (flipperAdapter.dataList.isEmpty()) {
                        tvListData.queryNextPage()
                    }
                }.disposeOn(fragment)
    }

    private fun showAccostSvga() {
        mSvgaDisposable?.dispose()
        mSvgaDisposable = SvgaData.createTaskFromAsset("accost.svga")
                .compose(RxUtils.ioToMainSingleScheduler())
                .subscribe({ entity ->
                    val drawable = SVGADrawable(entity)
                    fragment.accost_svga?.visibility = View.VISIBLE
                    fragment.accost_svga?.setImageDrawable(drawable)
                    fragment.accost_svga?.loops = 1
                    fragment.accost_svga?.startAnimation()
                    mSvgaDisposable?.dispose()
                }, { t -> t.printStackTrace() })
    }

    private fun requestBoardTop3() {
        BoardDataCreator.getBoardTop3(requestTag)
    }

    inner class FlipCountDownTimer constructor(millFuture: Long, interval: Long) : CountDownTimer(millFuture, interval) {
        override fun onFinish() {
            fragment.flip_lock?.setText("")
        }

        @SuppressLint("SetTextI18n")
        override fun onTick(millisUntilFinished: Long) {
            fragment.flip_lock?.setText("锁屏${millisUntilFinished / 1000 + 1}s")
        }
    }

    fun onDestroy() {
        flipTimer?.cancel()
    }
}